package com.quectel.core.module.alarm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.quectel.constant.OrderCodePrefixConstants;
import com.quectel.constant.core.alarm.AlarmConstants;
import com.quectel.constant.core.workorder.WorkOrderConstants;
import com.quectel.constant.global.SqlConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.util.aspect.lock.annotation.Lock;
import com.quectel.core.constants.RedisLockConstants;
import com.quectel.core.module.alarm.dao.AlarmEventRecordDao;
import com.quectel.core.module.alarm.dto.AlarmEventConfigDto;
import com.quectel.core.module.alarm.dto.AlarmEventRecordDto;
import com.quectel.core.module.alarm.dto.resp.EventGenerateResponse;
import com.quectel.core.module.alarm.entity.AlarmEventRecordEntity;
import com.quectel.core.module.alarm.service.AlarmEventConfigService;
import com.quectel.core.module.alarm.service.AlarmEventRecordService;
import com.quectel.core.module.alarm.service.AlarmLinkageRuleService;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.workorder.dto.WorkOrderDto;
import com.quectel.core.module.workorder.dto.WorkOrderLogDto;
import com.quectel.core.module.workorder.service.WorkOrderLogService;
import com.quectel.core.module.workorder.service.WorkOrderService;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.http.HttpRestUtils;
import com.quectel.util.kit.OrderUtils;
import com.quectel.util.kit.Snowflake;
import com.quectel.util.thread.ThreadPoolUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author jingyanmao
 * @date 2019-08-29 10:15:21
 */
@DubboService
@Slf4j
public class AlarmEventRecordServiceImpl implements AlarmEventRecordService, DisposableBean {

    @Autowired
    private DeviceService deviceService;
    @Autowired
    private AlarmEventRecordDao alarmEventRecordDao;
    @Autowired
    private AlarmEventConfigService alarmEventConfigService;
    @Autowired
    private WorkOrderService workOrderService;
    @Autowired
    private WorkOrderLogService workOrderLogService;
    @Autowired
    private AlarmLinkageRuleService alarmLinkageRuleService;


    private static final ThreadPoolExecutor ALARM_EVENT_RECORD_THREAD_POOL = ThreadPoolUtils.createCommonIOBoundPool("alarm_event_record_thread_pool_");

    private static final HttpRestUtils HTTP_REST_UTILS = HttpRestUtils.getDefaultInstance();

    @Override
    public void destroy() throws Exception {
        ThreadPoolUtils.shutdown(ALARM_EVENT_RECORD_THREAD_POOL);
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.WORK_ORDER_LOG_SEQ_INC_LOCK + "#{#alarmEventRecordDto.workOrderId}"
            }
    )
    @Transactional
    public void closeEventByRecord(AlarmEventRecordDto alarmEventRecordDto) {

        if (AlarmConstants.ALARM_EVENT_RECORD_COMPLETED.equals(alarmEventRecordDto.getStatus())) {
            return;
        }

        Date now = new Date();
        alarmEventRecordDto.setFinishTime(now);
        alarmEventRecordDto.setStatus(AlarmConstants.ALARM_EVENT_RECORD_COMPLETED);
        updateById(alarmEventRecordDto);

        if (alarmEventRecordDto.getWorkOrderId() != null && alarmEventRecordDto.getWorkOrderId() > 0) {

            //工单也需要恢复
            WorkOrderDto workOrderDto = workOrderService.selectById(alarmEventRecordDto.getWorkOrderId());
            if (workOrderDto == null) {
                return;
            }

            if (!WorkOrderConstants.ORDER_STATUS_FINISHED.equals(workOrderDto.getStatus())) {

                //自动关闭不存在处理人 所以需要判断是否是自动关闭 如果是自动关闭工单结束人就取当前结束人
                Long handlerId = SystemConstants.YES.equals(alarmEventRecordDto.getIsAutoClose())
                        ? workOrderDto.getHandlerId() : alarmEventRecordDto.getManualCloseEventRecordSysUserId();

                workOrderDto.setHandlerId(handlerId);
                workOrderDto.setStatus(WorkOrderConstants.ORDER_STATUS_FINISHED);
                workOrderDto.setFinishTime(now);
                workOrderService.updateById(workOrderDto);

                WorkOrderLogDto workOrderLogDto = new WorkOrderLogDto();
                workOrderLogDto.setWorkOrderId(workOrderDto.getId());
                workOrderLogDto.setHandlerId(handlerId);
                workOrderLogDto.setOrderStatus(WorkOrderConstants.ORDER_STATUS_FINISHED);
                if (SystemConstants.YES.equals(alarmEventRecordDto.getIsAutoClose())) {
                    workOrderLogDto.setOrderDes("事件记录自动关闭,触发工单处理流程完结,现在状态为已完成");
                } else {
                    workOrderLogDto.setOrderDes("事件记录手动关闭,触发工单处理流程完结，关闭用户为当前用户。现在状态为已完成");
                }
                workOrderLogDto.setCreateTime(now);
                workOrderLogService.save(workOrderLogDto);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoCloseEventByEventConfigId(Long deviceId, Long alarmEventConfigId) {

        AlarmEventConfigDto alarmEventConfigDto = alarmEventConfigService.selectById(alarmEventConfigId);
        if (alarmEventConfigDto == null || SystemConstants.NO.equals(alarmEventConfigDto.getIsAutoClose())) {
            log.info("事件的关闭不需要自动化 事件自动关闭停止");
            return;
        }

        List<AlarmEventRecordEntity> entityList = alarmEventRecordDao.selectList(new LambdaQueryWrapper<AlarmEventRecordEntity>()
                .eq(AlarmEventRecordEntity::getDeviceId, deviceId)
                .eq(AlarmEventRecordEntity::getEventConfigId, alarmEventConfigId).
                eq(AlarmEventRecordEntity::getStatus, AlarmConstants.ALARM_EVENT_RECORD_HANDLING)
        );

        for (AlarmEventRecordEntity alarmEventRecordEntity : entityList) {

            AlarmEventRecordDto alarmEventRecordDto = CopyUtils.copyObj(alarmEventRecordEntity, AlarmEventRecordDto.class);
            //设置自动关闭
            alarmEventRecordDto.setIsAutoClose(SystemConstants.YES);

            closeEventByRecord(alarmEventRecordDto);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public EventGenerateResponse generateEventRecord(AlarmEventRecordDto alarmEventRecordDto) {

        EventGenerateResponse response = new EventGenerateResponse();

        //先判断是否在时间段
        AlarmEventConfigDto alarmEventConfigDto = alarmEventRecordDto.getAlarmEventConfigDto();
        String nowTime = DateUtils.format(new Date(), DateUtils.FormatType.TIME_WITHOUT_SECONDS);
        if (alarmEventConfigDto.getTimeRangeRule() != null) {
            String[] split = alarmEventConfigDto.getTimeRangeRule().split("-");
            if (nowTime.compareTo(split[0]) < 0 || nowTime.compareTo(split[1]) > 0) {
                log.info("该时间段不生成事件");
                return response;
            }
        }
        if (AlarmConstants.ALARM_EVENT_CONFIG_GENERATE_ONE.equals(alarmEventConfigDto.getRepeatType())) {
            //只生成一次一次的
            Long uncompleteCount = alarmEventRecordDao.selectCount(new LambdaQueryWrapper<AlarmEventRecordEntity>()
                    .eq(AlarmEventRecordEntity::getEventConfigId, alarmEventConfigDto.getId()).eq(AlarmEventRecordEntity::getStatus, AlarmConstants.ALARM_EVENT_RECORD_HANDLING).last(SqlConstants.MYSQL_LIMIT_ONE));
            if (uncompleteCount <= 0) {
                //全部已经完成
                //生成该事件
                Long id = save(alarmEventRecordDto);
                alarmEventRecordDto.setId(id);

                Long workOrderId = generateOrder(alarmEventRecordDto.getDeviceDto(), alarmEventConfigDto, alarmEventRecordDto);
                if (workOrderId != null && workOrderId > 0) {
                    //生成工单后需要更新时间上的工单id 双向绑定
                    alarmEventRecordDto.setWorkOrderId(workOrderId);
                    updateById(alarmEventRecordDto);
                }
                if (StringUtils.isNotBlank(alarmEventConfigDto.getPushTypes()) && JacksonUtils.parseArray(alarmEventConfigDto.getPushTypes(), String.class).size() > 0) {
                    //有推送方式
                    response.setNeedPush(true);
                }
                response.setNeedLinkDevice(true);
                return response;

            }

        } else if (AlarmConstants.ALARM_EVENT_CONFIG_GENERATE_MORE.equals(alarmEventConfigDto.getRepeatType())) {
            //生成多次
            //直接保存
            Long id = save(alarmEventRecordDto);
            alarmEventRecordDto.setId(id);


            Long workOrderId = generateOrder(alarmEventRecordDto.getDeviceDto(), alarmEventConfigDto, alarmEventRecordDto);
            if (workOrderId != null && workOrderId > 0) {
                //生成工单后需要更新时间上的工单id 双向绑定
                alarmEventRecordDto.setWorkOrderId(workOrderId);
                updateById(alarmEventRecordDto);
            }
            if (StringUtils.isNotBlank(alarmEventConfigDto.getPushTypes()) && JacksonUtils.parseArray(alarmEventConfigDto.getPushTypes(), String.class).size() > 0) {
                //有推送方式
                response.setNeedPush(true);
            }
            response.setNeedLinkDevice(true);
            return response;
        }
        return response;

    }


    /**
     * 生成工单
     *
     * @param alarmEventConfigDto
     * @param alarmEventRecordDto
     * @return 工单的id为了双向绑定
     */
    private Long generateOrder(DeviceDto deviceDto, AlarmEventConfigDto alarmEventConfigDto, AlarmEventRecordDto alarmEventRecordDto) {
        if (SystemConstants.NO.equals(deviceDto.getWorkOrderSwitch())) {
            log.info("设备工单关闭 无需生成");
            return null;
        }
        if (SystemConstants.NO.equals(alarmEventConfigDto.getIsAutoGenerateWorkOrder())) {
            log.info("该事件无需生成工单");
            return null;
        }
        //生成工单
        WorkOrderDto workOrderDto = new WorkOrderDto();
        workOrderDto.setTenantId(deviceDto.getTenantId());
        workOrderDto.setVillageId(deviceDto.getVillageId());
        workOrderDto.setOrderSource(WorkOrderConstants.ORDER_SOURCE_ALARM);
        workOrderDto.setSourceId(alarmEventRecordDto.getId());
        workOrderDto.setOrderCode(OrderUtils.getOrderNo(OrderCodePrefixConstants.WORK_ORDER_PREFIX));
        workOrderDto.setDetail("由设备imei号为:" + deviceDto.getImei() + " 产生事件:" + alarmEventConfigDto.getName() + " 触发生成的工单");
        workOrderDto.setCreateTime(alarmEventRecordDto.getEventTime());
        workOrderDto.setHandlerId(alarmEventConfigDto.getWorkOrderHandlerId());
        workOrderDto.setStatus(WorkOrderConstants.ORDER_STATUS_HANDLING);
        Long workOrderId = workOrderService.save(workOrderDto);
        //生成log

        WorkOrderLogDto workOrderLogDto = new WorkOrderLogDto();
        workOrderLogDto.setWorkOrderId(workOrderId);
        workOrderLogDto.setHandlerId(workOrderDto.getHandlerId());
        workOrderLogDto.setOrderStatus(WorkOrderConstants.ORDER_STATUS_HANDLING);
        workOrderLogDto.setOrderDes("告警生成事件，由事件触发生成工单。现在状态为已接单，事件关闭该工单自动完结");
        workOrderLogDto.setCreateTime(alarmEventRecordDto.getEventTime());
        workOrderLogService.save(workOrderLogDto);

        return workOrderId;
    }


    @Override
    public List<AlarmEventRecordDto> queryList(Map<String, Object> params) {
        List<AlarmEventRecordEntity> list = alarmEventRecordDao.queryList(params);
        List<AlarmEventRecordDto> result = CopyUtils.copyList(list, AlarmEventRecordDto.class);
        for (AlarmEventRecordDto alarmEventRecordDto : result) {
            if (alarmEventRecordDto.getDeviceId() != null) {
                alarmEventRecordDto.setDeviceDto(deviceService.selectCacheById(alarmEventRecordDto.getDeviceId()));
            }
        }
        return result;
    }

    @Override
    public int queryTotal(Map<String, Object> params) {
        return alarmEventRecordDao.queryTotal(params);
    }

    @Override
    public Long save(AlarmEventRecordDto dto) {
        AlarmEventRecordEntity entity = CopyUtils.copyObj(dto, AlarmEventRecordEntity.class);
        entity.setId(Snowflake.nextId());
        alarmEventRecordDao.insert(entity);
        return entity.getId();
    }

    @Override
    public void updateById(AlarmEventRecordDto dto) {
        AlarmEventRecordEntity entity = CopyUtils.copyObj(dto, AlarmEventRecordEntity.class);
        alarmEventRecordDao.updateById(entity);
    }

    @Override
    public AlarmEventRecordDto selectById(Long id) {
        AlarmEventRecordEntity entity = alarmEventRecordDao.selectById(id);
        return CopyUtils.copyObj(entity, AlarmEventRecordDto.class);
    }

    @Override
    public void deleteById(Long id) {
        alarmEventRecordDao.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(Long[] ids) {
        for (Long id : ids) {
            deleteById(id);
        }
    }


}
